// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using ClangSharp.Interop;
using static ClangSharp.Interop.CX_TemplateArgumentDependence;
using static ClangSharp.Interop.CXTemplateArgumentKind;

namespace ClangSharp;

public sealed unsafe class TemplateArgument : IDisposable
{
    private readonly ValueLazy<ValueDecl> _asDecl;
    private readonly ValueLazy<Expr> _asExpr;
    private readonly ValueLazy<TemplateName> _asTemplate;
    private readonly ValueLazy<TemplateName> _asTemplateOrTemplatePattern;
    private readonly ValueLazy<Type> _asType;
    private readonly ValueLazy<Type> _integralType;
    private readonly ValueLazy<Type> _nonTypeTemplateArgumentType;
    private readonly ValueLazy<Type> _nullPtrType;
    private readonly LazyList<TemplateArgument> _packElements;
    private readonly ValueLazy<TemplateArgument> _packExpansionPattern;
    private readonly ValueLazy<Type> _paramTypeForDecl;
    private readonly ValueLazy<TranslationUnit> _translationUnit;

    internal TemplateArgument(CX_TemplateArgument handle)
    {
        Handle = handle;

        _translationUnit = new ValueLazy<TranslationUnit>(() => TranslationUnit.GetOrCreate(Handle.tu));

        _asDecl = new ValueLazy<ValueDecl>(() =>  TranslationUnit.GetOrCreate<ValueDecl>(Handle.AsDecl));
        _asExpr = new ValueLazy<Expr>(() => TranslationUnit.GetOrCreate<Expr>(Handle.AsExpr));
        _asTemplate = new ValueLazy<TemplateName>(() => TranslationUnit.GetOrCreate(Handle.AsTemplate));
        _asTemplateOrTemplatePattern = new ValueLazy<TemplateName>(() => TranslationUnit.GetOrCreate(Handle.AsTemplateOrTemplatePattern));
        _asType = new ValueLazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.AsType));
        _integralType = new ValueLazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.IntegralType));
        _nonTypeTemplateArgumentType = new ValueLazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.NonTypeTemplateArgumentType));
        _nullPtrType = new ValueLazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.NullPtrType));
        _packExpansionPattern = new ValueLazy<TemplateArgument>(() => TranslationUnit.GetOrCreate(Handle.PackExpansionPattern));
        _packElements = LazyList.Create<TemplateArgument>(Handle.NumPackElements, (i) => TranslationUnit.GetOrCreate(Handle.GetPackElement(unchecked((uint)i))));
        _paramTypeForDecl = new ValueLazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.ParamTypeForDecl));
    }

    ~TemplateArgument() => Dispose(isDisposing: false);

    public ValueDecl AsDecl => _asDecl.Value;

    public Expr AsExpr => _asExpr.Value;

    public long AsIntegral => Handle.AsIntegral;

    public TemplateName AsTemplate => _asTemplate.Value;

    public TemplateName AsTemplateOrTemplatePattern => _asTemplateOrTemplatePattern.Value;

    public Type AsType => _asType.Value;

    public bool ContainsUnexpandedParameterPack => (Dependence & CX_TAD_UnexpandedPack) != 0;

    public CX_TemplateArgumentDependence Dependence => Handle.Dependence;

    public Type IntegralType => _integralType.Value;

    public bool IsDependent => (Dependence & CX_TAD_Dependent) != 0;

    public bool IsInstantiationDependent => (Dependence & CX_TAD_Instantiation) != 0;

    public bool IsNull => Kind == CXTemplateArgumentKind_Null;

    public bool IsPackExpansion
    {
        get
        {
            switch (Kind)
{
                case CXTemplateArgumentKind_Null:
                case CXTemplateArgumentKind_Declaration:
                case CXTemplateArgumentKind_Integral:
                case CXTemplateArgumentKind_Pack:
                case CXTemplateArgumentKind_Template:
                case CXTemplateArgumentKind_NullPtr:
                {
                    return false;
                }

                case CXTemplateArgumentKind_TemplateExpansion:
                {
                    return true;
                }

                case CXTemplateArgumentKind_Type:
                {
                    return AsType is PackExpansionType;
                }

                case CXTemplateArgumentKind_Expression:
                {
                    return AsExpr is PackExpansionExpr;
                }
            }

            Debug.Fail("Invalid TemplateArgument Kind!");
            return false;
        }
    }

    public CX_TemplateArgument Handle { get; }

    public CXTemplateArgumentKind Kind => Handle.kind;

    public Type NonTypeTemplateArgumentType => _nonTypeTemplateArgumentType.Value;

    public Type NullPtrType => _nullPtrType.Value;

    public IReadOnlyList<TemplateArgument> PackElements => _packElements;

    public TemplateArgument PackExpansionPattern => _packExpansionPattern.Value;

    public Type ParamTypeForDecl => _paramTypeForDecl.Value;

    public TranslationUnit TranslationUnit => _translationUnit.Value;

    public void Dispose()
    {
        Dispose(isDisposing: true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool isDisposing) => Handle.Dispose();
}
